package pers.dawnyang.config.shiro;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import pers.dawnyang.common.constant.Const;

/***
 * shiro配置
 * 
 * @author dawn yang create
 *         2020年5月25日下午4:16:03
 * 
 */
@Configuration
public class ShiroConfig {

  @Value("${shiro.config.rememberme}")
  private Boolean rememberme;

  @Bean
  public ShiroFilterFactoryBean shirFilter(DefaultWebSecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);

    /**
     * 添加自定义拦截器，处理session超时问题
     */
    HashMap<String, Filter> myFilters = new HashMap<>(16);
    myFilters.put("userAuth", new UserAuthFilter());
    shiroFilterFactoryBean.setFilters(myFilters);

    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

    // 不需要登陆验证的请求
    List<String> anonUrls = Const.ANNO_URL;
    for (String str : anonUrls) {
      filterChainDefinitionMap.put(str, "anon");
    }

    // 其他的所有请求需要登陆验证
    if (rememberme) {
      // 如需启用rememberme功能，需开启user权限，关闭authc
      filterChainDefinitionMap.put("/**", "user");
    } else {
      // 如需开启authc拦截，需关闭rememberme功能
      filterChainDefinitionMap.put("/**", "authc");
    }

    // 自定义拦截器需放在authc后面才会生效
    filterChainDefinitionMap.put("/**", "userAuth");

    shiroFilterFactoryBean.setLoginUrl("/login");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    return shiroFilterFactoryBean;
  }

  @Bean
  public EhCacheManager getEhCacheManager() {
    EhCacheManager em = new EhCacheManager();
    em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
    return em;
  }

  @Bean
  public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
    daap.setProxyTargetClass(true);
    return daap;
  }

  @Bean
  public DefaultWebSessionManager getDefaultWebSessionManager() {
    DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
    defaultWebSessionManager.setSessionDAO(getMemorySessionDAO());
    // 会话过期时间，单位：毫秒(在无操作时开始计时)
    defaultWebSessionManager.setGlobalSessionTimeout(Const.SEESION_TIMEOUT);
    // 是否开启删除无效的session对象 默认为true
    defaultWebSessionManager.setDeleteInvalidSessions(true);
    // 是否开启定时调度器进行检测过期session 默认为true
    defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
    defaultWebSessionManager.setSessionIdCookieEnabled(true);
    defaultWebSessionManager.setSessionIdCookie(getSimpleCookie());
    // 防止第一次重定向url带有jsessionid导致400错误
    defaultWebSessionManager.setSessionIdUrlRewritingEnabled(false);
    return defaultWebSessionManager;
  }

  @Bean(name = "memorySessionDAO")
  public MemorySessionDAO getMemorySessionDAO() {
    MemorySessionDAO memorySessionDAO = new MemorySessionDAO();
    memorySessionDAO.setSessionIdGenerator(javaUuidSessionIdGenerator());
    return memorySessionDAO;
  }

  @Bean
  public JavaUuidSessionIdGenerator javaUuidSessionIdGenerator() {
    return new JavaUuidSessionIdGenerator();
  }

  /**
   * session自定义cookie名
   * 
   * @return
   */
  @Bean
  public SimpleCookie getSimpleCookie() {
    SimpleCookie simpleCookie = new SimpleCookie();
    simpleCookie.setName("shiro.session.id");
    simpleCookie.setPath("/");
    return simpleCookie;
  }

  /**
   * Shiro生命周期处理器:
   * 用于在实现了Initializable接口的Shiro
   * bean初始化时调用Initializable接口回调(例如:UserRealm)
   * 在实现了Destroyable接口的Shiro bean销毁时调用
   * Destroyable接口回调(例如:DefaultSecurityManager)
   */
  @Bean
  public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
  }

  @Bean
  public DefaultWebSecurityManager getDefaultWebSecurityManager(ShiroUserRealm userRealm) {
    DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
    dwsm.setRealm(userRealm);
    // <!-- 用户授权/认证信息Cache, 采用EhCache 缓存 -->
    dwsm.setCacheManager(getEhCacheManager());
    dwsm.setRememberMeManager(rememberMeManager());
    dwsm.setSessionManager(getDefaultWebSessionManager());
    return dwsm;
  }

  @Bean
  public ShiroUserRealm userRealm(EhCacheManager cacheManager) {
    ShiroUserRealm userRealm = new ShiroUserRealm();
    userRealm.setCacheManager(cacheManager);
    // 设置自定义加密方法
    userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
    return userRealm;
  }

  // 开启shrio aop注解支持
  @Bean
  public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(
      SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
    aasa.setSecurityManager(securityManager);
    return aasa;
  }

  @Bean
  public HashedCredentialsMatcher hashedCredentialsMatcher() {
    HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
    hashedCredentialsMatcher.setHashAlgorithmName("md5");
    // 散列的次数，比如散列两次，相当于md5(md5(""));
    hashedCredentialsMatcher.setHashIterations(Const.MD5_NUM);
    return hashedCredentialsMatcher;
  }

  /**
   * rememberMe管理器
   */
  @Bean
  public CookieRememberMeManager rememberMeManager() {
    CookieRememberMeManager manager = new CookieRememberMeManager();
    manager.setCipherKey(getGenerateCipherKey());
    // manager.setCipherKey(Base64.decode("WcfHGU25gNnTxTlmJMeSpw=="));
    manager.setCookie(rememberMeCookie());
    return manager;
  }

  /**
   * 记住密码Cookie
   */
  @Bean
  public SimpleCookie rememberMeCookie() {
    SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
    simpleCookie.setHttpOnly(true);
    simpleCookie.setMaxAge(7 * 24 * 60 * 60);
    simpleCookie.setPath("/");
    return simpleCookie;
  }

  /**
   * 防止加密串泄露导致的反序列化漏洞
   * 
   * @return
   */
  @Bean
  public byte[] getGenerateCipherKey() {
    return GenerateCipherKey.generateNewKey();
  }
}
